//
//  SocketManager.m
//  Common
//
//  Created by shiqing guo on 2018/9/27.
//  Copyright © 2018年 www.bitkinetic.com. All rights reserved.
//

#import "SocketManager.h"
#import "SRWebSocket.h"
#import "Macro.h"
#import <AFNetworking/AFNetworking.h>

@interface SocketManager () <SRWebSocketDelegate>
@property (nonatomic, strong) SRWebSocket *webSocket;
@property (nonatomic, assign) SocketStatus socketStatus;
@property (nonatomic, weak) NSTimer *timer;
@property (nonatomic, copy) NSString *urlString;

@property (nonatomic, assign) NSUInteger reConnectTime;

// 网络状态检查定时器
@property (nonatomic, weak) NSTimer *netWorkTestingTimer;
@end

@implementation SocketManager

+ (instancetype)shareManager{
    
    static SocketManager *instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[SocketManager alloc] init];
        instance.reconnectAllTime = 64;
    });
    return instance;
}

- (void)open:(NSString *)urlStr connect:(SocketDidConnectBlock)connect receive:(SocketDidReceiveBlock)receive failure:(SocketDidFailBlock)failure{
    
    SM.connect = connect;
    SM.receive = receive;
    SM.failure = failure;
    [self open:urlStr];
}

- (void)close:(SocketDidCloseBlock)close{
    
    SM.close = close;
    [self closeSocket];
}

// Send a UTF8 String or Data.
- (void)send:(id)data{
    
//    NSLog(@"-- 发送消息 状态值为%ld", SM.socketStatus);
    switch (SM.socketStatus) {
        case SocketStatusConnected:
        case SocketStatusReceived:{
//            NSLog(@"发送中。。。");
            if (SR_OPEN == self.webSocket.readyState)
            {
                [self.webSocket send:data];
            }
            break;
        }
        case SocketStatusFailed:
//            NSLog(@"发送失败");
            break;
        case SocketStatusClosedByServer:
//            NSLog(@"服务端主动关闭");
            break;
        case SocketStatusClosedByUser:
//            NSLog(@"客户端自己关闭");
            break;
    }
}

#pragma mark -- private method
- (void)open:(id)params{
    
    NSString *urlStr = nil;
    if ([params isKindOfClass:[NSString class]]) {
        urlStr = (NSString *)params;
    }
    else if([params isKindOfClass:[NSTimer class]]){
        NSTimer *timer = (NSTimer *)params;
        urlStr = [timer userInfo];
    }
    SM.urlString = urlStr;
    [self.webSocket close];
    self.webSocket.delegate = nil;
    
//    NSLog(@"\n\n -- 重新创建socket -- ");
    self.webSocket = [[SRWebSocket alloc] initWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:urlStr]]];
    self.webSocket.delegate = self;
    [self.webSocket open];
}

- (void)closeSocket{
//    NSLog(@"\n\n -- 主动关闭socket -- ");
    if (self.webSocket)
    {
        [_webSocket close];
        _webSocket = nil;
    }

    // 断开连接时销毁心跳
    [self destoryHeartBeat];
}

- (void)reConnect{

//    NSLog(@"\n\n正在重连。。。。");
    
    // 已经连接上，或者接收到回复，就不重连了
    if (SocketStatusConnected == SM.socketStatus || SocketStatusReceived == SM.socketStatus)
    {
//        NSLog(@"\n\n 当前正在重连或者已经收到回复了 \n\n");
        return;
    }
    
    [self open:self.urlString];

    // 超过一分钟就不再重连 所以只会重连5次 2^5 = 64
//    if (self.reConnectTime > self.reconnectAllTime) return;

//    WEAKSELF
//    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(self.reConnectTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//        weakSelf.webSocket = nil;
//        NSLog(@"重链url=%@", weakSelf.urlString);
//        [weakSelf open:weakSelf.urlString];
//    });
//
//    //重连时间2的指数级增长
//    if (0 == self.reConnectTime) self.reConnectTime = 2;
//    else self.reConnectTime *= 2;
//    NSLog(@"重连时间=%ld", self.reConnectTime);
}

#pragma mark -- SRWebSocketDelegate
- (void)webSocketDidOpen:(SRWebSocket *)webSocket{

    NSLog(@"Websocket Connected");
//    NSLog(@"\n\n -- 连接成功 -- ");
    SM.socketStatus = SocketStatusConnected;
    SM.connect ? SM.connect() : nil;
    self.reConnectTime = 0;
}

- (void)setSocketStatus:(SocketStatus)socketStatus
{
    _socketStatus = socketStatus;
//    NSLog(@"-- 当前状态值为%ld", socketStatus);
}

- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error{

    NSLog(@":( Websocket Failed With Error %@", error);
    SM.socketStatus = SocketStatusFailed;
    SM.failure?SM.failure(error):nil;
    
    // 判断网络环境状态
    [self noNetWorkStartTestingTimer];
}

- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message{
 
    NSLog(@":( Websocket Receive With message %@", message);
    SM.socketStatus = SocketStatusReceived;
    SM.receive?SM.receive(message, SocketReceiveTypeForMessage): nil;
}

- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean{

//    NSLog(@"\n\n -- 收到socket关闭响应 -- ");
    SM.close?SM.close(code,reason,wasClean):nil;
    
    if (reason) {
        SM.socketStatus = SocketStatusClosedByServer;
        [self reConnect];
    }
    else{
        SM.socketStatus = SocketStatusClosedByUser;
    }

    // 断开连接时销毁心跳
    [self destoryHeartBeat];
}

- (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload{
//    NSLog(@"收到了服务器的心跳响应");
    SM.receive?SM.receive(pongPayload,SocketReceiveTypeForPong):nil;
}

- (void)dealloc{
    [self closeSocket];
}

#pragma mark - 心跳机制
// 初始化心跳
- (void)initHeartBeat
{
    WEAKSELF
    MainQueue(^{
        [weakSelf destoryHeartBeat];
        // 6秒发送一个心跳
        weakSelf.timer = [NSTimer scheduledTimerWithTimeInterval:6.0 target:weakSelf selector:@selector(sendPing) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:weakSelf.timer forMode:NSRunLoopCommonModes];
    });
}

- (void)sendPing
{
    [self ping];
}

// 取消心跳
- (void)destoryHeartBeat {
    
    if (self.timer)
    {
        [self.timer invalidate];
        self.timer = nil;
    }
}

// 发送心跳
- (void)ping
{
    WEAKSELF
    MainQueue(^{
        if(SR_OPEN == weakSelf.webSocket.readyState)
        {
            [weakSelf.webSocket sendPing:nil];
//            NSLog(@"心跳发送出去了");
        }
    });
}

#pragma mark - 网络状态检查机制

- (void)noNetWorkStartTestingTimer
{
    WEAKSELF
    MainQueue(^{
        weakSelf.netWorkTestingTimer = [NSTimer scheduledTimerWithTimeInterval:3.0 target:weakSelf selector:@selector(noNetWorkStartTesting) userInfo:nil repeats:YES];
        [[NSRunLoop currentRunLoop] addTimer:weakSelf.netWorkTestingTimer forMode:NSDefaultRunLoopMode];
    });
}

- (void)noNetWorkStartTesting
{
    BOOL isAbility = [[self class] netWorkReachability];
    if (isAbility) {
        if (self.netWorkTestingTimer)
        {
            [self.netWorkTestingTimer invalidate];
            self.netWorkTestingTimer = nil;
        }
        [self reConnect];
    }
}

/**
 监测网络的可链接性
 @return 是否可达
 */
+ (BOOL)netWorkReachability{
    __block BOOL netState = YES;
    [[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        switch (status) {
            case AFNetworkReachabilityStatusNotReachable:
            case AFNetworkReachabilityStatusUnknown:
                netState = NO;
                break;
            case AFNetworkReachabilityStatusReachableViaWiFi:
            case AFNetworkReachabilityStatusReachableViaWWAN:
                netState = YES;
                break;
                
            default:
                break;
        }
        NSLog(@"Reachability: %@", AFStringFromNetworkReachabilityStatus(status));
    }];
    [[AFNetworkReachabilityManager sharedManager] startMonitoring];
    return netState;
}

@end
